lwip socket探秘之accept

您所在的位置:网站首页 lwip socket lwip socket探秘之accept

lwip socket探秘之accept

#lwip socket探秘之accept| 来源: 网络整理| 查看: 265

lwip socket探秘之accept 一个基本的socket建立顺序是 Server端: socket() bind() listen() accept() recv() Client端: socket() connect() send()   本文着重介绍Server端的accept()过程。   上一篇我们已经分析了listen()过程,listen()过程新建了pcb并把它放到了tcp_listen_pcbs这个链表里。 接下来,Client端通过Server绑定的地址和端口号(通过bind绑定),给Server发包。Server收到了Client过来的TCP包后,如何记住这个Client,并且接下来会做什么呢?这些就是这篇小文分析的内容。   首先Server端TCP层接到了Client来的TCP segment。我们从lwip中TCP层rx的入口开始讲起。 tcp_input()是TCP层rx数据的入口,如下面代码段红色注释部分所示。 注意传进来的参数p,虽然这是TCP层入口,但它内部的p->payload是没有剥离ip头的,原因就是,TCP层处理socket时,是需要remote端的ip的。这一点从tcp_input()函数内部就能看出来。 代码段如下: 1 /** 2 * The initial input processing of TCP. It verifies the TCP header, demultiplexes 3 * the segment between the PCBs and passes it on to tcp_process(), which implements 4 * the TCP finite state machine. This function is called by the IP layer (in 5 * ip_input()). 6 * 7 * @param p received TCP segment to process (p->payload pointing to the IP header) 8 * @param inp network interface on which this segment was received 9 */ 10 void 11 tcp_input(struct pbuf *p, struct netif *inp) 12 { 13 struct tcp_pcb *pcb, *prev; 14 struct tcp_pcb_listen *lpcb; 15 u8_t hdrlen; 16 err_t err; 17 18 PERF_START; 19 20 TCP_STATS_INC(tcp.recv); 21 snmp_inc_tcpinsegs(); 22 23 iphdr = p->payload; // 得到了IP header 24 tcphdr = (struct tcp_hdr *)((u8_t *)p->payload + IPH_HL(iphdr) * 4); // 得到了TCP header 25 26 ............ 27 28 /* Convert fields in TCP header to host byte order. */ 29 tcphdr->src = ntohs(tcphdr->src); // 把tcp header的一些内容从网络字节序转成主机字节序。可以猜测ip header已经在ip层被转过了。 30 tcphdr->dest = ntohs(tcphdr->dest); 31 seqno = tcphdr->seqno = ntohl(tcphdr->seqno); 32 ackno = tcphdr->ackno = ntohl(tcphdr->ackno); 33 tcphdr->wnd = ntohs(tcphdr->wnd); 34 35 .................. 36 37 //下面出现了3个pcb链表,分别是tcp_active_pcbs、tcp_tw_pcbs和tcp_listen_pcbs。 38 for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { 39 LWIP_ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED); 40 LWIP_ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT); 41 LWIP_ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN); 42 if (pcb->remote_port == tcphdr->src && 43 pcb->local_port == tcphdr->dest && 44 ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)) && 45 ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest))) { 46 47 /* Move this PCB to the front of the list so that subsequent 48 lookups will be faster (we exploit locality in TCP segment 49 arrivals). */ 50 LWIP_ASSERT("tcp_input: pcb->next != pcb (before cache)", pcb->next != pcb); 51 if (prev != NULL) { 52 prev->next = pcb->next; 53 pcb->next = tcp_active_pcbs; 54 tcp_active_pcbs = pcb; 55 } 56 LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb); 57 break; 58 } 59 prev = pcb; 60 } 61 62 if (pcb == NULL) { 63 /* If it did not go to an active connection, we check the connections 64 in the TIME-WAIT state. */ 65 for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { 66 LWIP_ASSERT("tcp_input: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); 67 if (pcb->remote_port == tcphdr->src && 68 pcb->local_port == tcphdr->dest && 69 ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)) && 70 ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest))) { 71 /* We don't really care enough to move this PCB to the front 72 of the list since we are not very likely to receive that 73 many segments for connections in TIME-WAIT. */ 74 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for TIME_WAITing connection.\n")); 75 tcp_timewait_input(pcb); 76 pbuf_free(p); 77 return; 78 } 79 } 80 81 //我们暂时只关心tcp_listen_pcbs这个链表。这个链表包含了所有处在listen状态的pcb。一个listen状态的pcb是什么时候把自己注册进tcp_listen_pcbs这个链表的?这个待会讲到。 82 /* Finally, if we still did not get a match, we check all PCBs that 83 are LISTENing for incoming connections. */ 84 prev = NULL; 85 for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { 86 if ((ip_addr_isany(&(lpcb->local_ip)) || // 如果pcb->local_ip是0或者NULL,意味着接收任何ip的连接请求 87 ip_addr_cmp(&(lpcb->local_ip), &(iphdr->dest))) && // 或者pcb->local_ip和接收到的tcp segment的ip地址一致,并且pcb->local_port和接收到的tcp segment的port号也一致,认为找到了之前注册的、并且是remote client发过来的这个tcp segment的目标的pcb。 88 lpcb->local_port == tcphdr->dest) { 89 /* Move this PCB to the front of the list so that subsequent 90 lookups will be faster (we exploit locality in TCP segment 91 arrivals). */ 92 if (prev != NULL) { 93 ((struct tcp_pcb_listen *)prev)->next = lpcb->next; 94 /* our successor is the remainder of the listening list */ 95 lpcb->next = tcp_listen_pcbs.listen_pcbs; 96 /* put this listening pcb at the head of the listening list */ 97 tcp_listen_pcbs.listen_pcbs = lpcb; 98 } 99 100 LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n")); 101 tcp_listen_input(lpcb); // 得到server正在listen状态的pcb后,用刚收到的remote tcp segment更新pcb,马上会分析这个函数 102 pbuf_free(p); 103 return; 104 } 105 prev = (struct tcp_pcb *)lpcb; 106 } 107 }   tcp_listen_input()如下: 1 /** 2 * Called by tcp_input() when a segment arrives for a listening 3 * connection (from tcp_input()). 4 * 5 * @param pcb the tcp_pcb_listen for which a segment arrived 6 * @return ERR_OK if the segment was processed 7 * another err_t on error 8 * 9 * @note the return value is not (yet?) used in tcp_input() 10 * @note the segment which arrived is saved in global variables, therefore only the pcb 11 * involved is passed as a parameter to this function 12 */ 13 static err_t 14 tcp_listen_input(struct tcp_pcb_listen *pcb) 15 { 16 struct tcp_pcb *npcb; 17 err_t rc; 18 19 /* In the LISTEN state, we check for incoming SYN segments, 20 creates a new PCB, and responds with a SYN|ACK. */ 21 if (flags & TCP_ACK) { 22 /* For incoming segments with the ACK flag set, respond with a 23 RST. */ 24 LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_listen_input: ACK in LISTEN, sending reset\n")); 25 tcp_rst(ackno + 1, seqno + tcplen, 26 &(iphdr->dest), &(iphdr->src), 27 tcphdr->dest, tcphdr->src); 28 } else if (flags & TCP_SYN) { 29 LWIP_DEBUGF(TCP_DEBUG, ("TCP connection request %"U16_F" -> %"U16_F".\n", tcphdr->src, tcphdr->dest)); 30 #if TCP_LISTEN_BACKLOG 31 if (pcb->accepts_pending >= pcb->backlog) { 32 LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: listen backlog exceeded for port %"U16_F"\n", tcphdr->dest)); 33 return ERR_ABRT; 34 } 35 #endif /* TCP_LISTEN_BACKLOG */ 36 npcb = tcp_alloc(pcb->prio); // allocate一个新的pcb,待会会放入active pcb链表 37 /* If a new PCB could not be created (probably due to lack of memory), 38 we don't do anything, but rely on the sender will retransmit the 39 SYN at a time when we have more memory available. */ 40 if (npcb == NULL) { 41 LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: could not allocate PCB\n")); 42 TCP_STATS_INC(tcp.memerr); 43 return ERR_MEM; 44 } 45 #if TCP_LISTEN_BACKLOG 46 pcb->accepts_pending++; 47 #endif /* TCP_LISTEN_BACKLOG */ 48 /* Set up the new PCB. */ 49 ip_addr_set(&(npcb->local_ip), &(iphdr->dest)); // pcb里存好server端自己的ip 50 npcb->local_port = pcb->local_port; // pcb里存好server端自己的port号 51 ip_addr_set(&(npcb->remote_ip), &(iphdr->src)); // pcb里指定client端的ip 52 npcb->remote_port = tcphdr->src; // pcb里指定client端的port号 53 npcb->state = SYN_RCVD; // pcb的状态变成了SYN_RCVD 54 npcb->rcv_nxt = seqno + 1; 55 npcb->rcv_ann_right_edge = npcb->rcv_nxt; 56 npcb->snd_wnd = tcphdr->wnd; 57 npcb->ssthresh = npcb->snd_wnd; 58 npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */ 59 npcb->callback_arg = pcb->callback_arg; 60 #if LWIP_CALLBACK_API 61 npcb->accept = pcb->accept; 62 #endif /* LWIP_CALLBACK_API */ 63 /* inherit socket options */ 64 npcb->so_options = pcb->so_options & (SOF_DEBUG|SOF_DONTROUTE|SOF_KEEPALIVE|SOF_OOBINLINE|SOF_LINGER); 65 /* Register the new PCB so that we can begin receiving segments 66 for it. */ 67 TCP_REG(&tcp_active_pcbs, npcb); // 把新pcb加入active pcbs链表,以后这个pcb专门为server端与remote ip对应的这个client端之间的通信服务 68 69 /* Parse any options in the SYN. */ 70 tcp_parseopt(npcb); 71 #if TCP_CALCULATE_EFF_SEND_MSS 72 npcb->mss = tcp_eff_send_mss(npcb->mss, &(npcb->remote_ip)); 73 #endif /* TCP_CALCULATE_EFF_SEND_MSS */ 74 75 snmp_inc_tcppassiveopens(); 76 77 /* Send a SYN|ACK together with the MSS option. */ 78 rc = tcp_enqueue(npcb, NULL, 0, TCP_SYN | TCP_ACK, 0, TF_SEG_OPTS_MSS // tx一个SYN|ACK 79 #if LWIP_TCP_TIMESTAMPS 80 /* and maybe include the TIMESTAMP option */ 81 | (npcb->flags & TF_TIMESTAMP ? TF_SEG_OPTS_TS : 0) 82 #endif 83 ); 84 if (rc != ERR_OK) { 85 tcp_abandon(npcb, 0); 86 return rc; 87 } 88 return tcp_output(npcb); 89 } 90 return ERR_OK; 91 }

 

至此,产生了一个新pcb,这个pcb处在SYN_RCVD状态。这个pcb被加入了active pcbs链表。 如果刚才的client又发送了tcp segment过来,那么接收的流程又跟上面讲的有所不同。还是从tcp_input开始分析: 1 void 2 tcp_input(struct pbuf *p, struct netif *inp) 3 { 4 struct tcp_pcb *pcb, *prev; 5 struct tcp_pcb_listen *lpcb; 6 u8_t hdrlen; 7 err_t err; 8 9 PERF_START; 10 11 TCP_STATS_INC(tcp.recv); 12 snmp_inc_tcpinsegs(); 13 14 iphdr = p->payload; // 得到了IP header 15 tcphdr = (struct tcp_hdr *)((u8_t *)p->payload + IPH_HL(iphdr) * 4); // 得到了TCP header 16 17 ............ 18 19 /* Convert fields in TCP header to host byte order. */ 20 tcphdr->src = ntohs(tcphdr->src); // 把tcp header的一些内容从网络字节序转成主机字节序。可以猜测ip header已经在ip层被转过了。 21 tcphdr->dest = ntohs(tcphdr->dest); 22 seqno = tcphdr->seqno = ntohl(tcphdr->seqno); 23 ackno = tcphdr->ackno = ntohl(tcphdr->ackno); 24 tcphdr->wnd = ntohs(tcphdr->wnd); 25 26 .................. 27 28 //下面出现了3个pcb链表,分别是tcp_active_pcbs、tcp_tw_pcbs和tcp_listen_pcbs。 29 // 这一次,tcp segment对应的client-server专属pcb能够在 tcp_active_pcbs链表里找到。 30 for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { 31 LWIP_ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED); 32 LWIP_ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT); 33 LWIP_ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN); 34 if (pcb->remote_port == tcphdr->src && // 这个tcp segment对应的client和server信息都能和某个pcb一致,则找到了这个pcb 35 pcb->local_port == tcphdr->dest && 36 ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)) && 37 ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest))) { 38 39 /* Move this PCB to the front of the list so that subsequent 40 lookups will be faster (we exploit locality in TCP segment 41 arrivals). */ 42 LWIP_ASSERT("tcp_input: pcb->next != pcb (before cache)", pcb->next != pcb); 43 if (prev != NULL) { 44 prev->next = pcb->next; 45 pcb->next = tcp_active_pcbs; 46 tcp_active_pcbs = pcb; 47 } 48 LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb); 49 break; 50 } 51 prev = pcb; 52 } 53 54 ............. 55 56 if (pcb != NULL) { 57 err = tcp_process(pcb); 58 }

 

tcp_process(): 1 static err_t 2 tcp_process(struct tcp_pcb *pcb) 3 { 4 .............. 5 case SYN_RCVD: 6 if (flags & TCP_ACK) { 7 /* expected ACK number? */ 8 if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) { 9 u16_t old_cwnd; 10 pcb->state = ESTABLISHED; 11 LWIP_DEBUGF(TCP_DEBUG, ("TCP connection established %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); 12 #if LWIP_CALLBACK_API 13 LWIP_ASSERT("pcb->accept != NULL", pcb->accept != NULL); 14 #endif 15 /* Call the accept function. */ 16 TCP_EVENT_ACCEPT(pcb, ERR_OK, err); // 这里会调到accept_function函数 17 if (err != ERR_OK) { 18 /* If the accept function returns with an error, we abort 19 * the connection. */ 20 tcp_abort(pcb); 21 return ERR_ABRT; 22 } 23 old_cwnd = pcb->cwnd; 24 /* If there was any data contained within this ACK, 25 * we'd better pass it on to the application as well. */ 26 tcp_receive(pcb); 27 28 /* Prevent ACK for SYN to generate a sent event */ 29 if (pcb->acked != 0) { 30 pcb->acked--; 31 } 32 33 pcb->cwnd = ((old_cwnd == 1) ? (pcb->mss * 2) : pcb->mss); 34 35 if (recv_flags & TF_GOT_FIN) { 36 tcp_ack_now(pcb); 37 pcb->state = CLOSE_WAIT; 38 } 39 } 40 /* incorrect ACK number */ 41 else { 42 /* send RST */ 43 tcp_rst(ackno, seqno + tcplen, &(iphdr->dest), &(iphdr->src), 44 tcphdr->dest, tcphdr->src); 45 } 46 } else if ((flags & TCP_SYN) && (seqno == pcb->rcv_nxt - 1)) { 47 /* Looks like another copy of the SYN - retransmit our SYN-ACK */ 48 tcp_rexmit(pcb); 49 } 50 break; 51 .................... 52 } 1 /** 2 * Accept callback function for TCP netconns. 3 * Allocates a new netconn and posts that to conn->acceptmbox. 4 * 5 * @see tcp.h (struct tcp_pcb_listen.accept) for parameters and return value 6 */ 7 // 注意accept_function的参数是 8 // arg:server端负责listen的那个sock的sock->conn, 9 // newpcb:listen到tcp segment后为特定的client-server创建的pcb 10 // err:err 11 static err_t 12 accept_function(void *arg, struct tcp_pcb *newpcb, err_t err) 13 { 14 struct netconn *newconn; 15 struct netconn *conn; 16 17 #if API_MSG_DEBUG 18 #if TCP_DEBUG 19 tcp_debug_print_state(newpcb->state); 20 #endif /* TCP_DEBUG */ 21 #endif /* API_MSG_DEBUG */ 22 conn = (struct netconn *)arg; 23 24 LWIP_ERROR("accept_function: invalid conn->acceptmbox", 25 conn->acceptmbox != SYS_MBOX_NULL, return ERR_VAL;); 26 27 /* We have to set the callback here even though 28 * the new socket is unknown. conn->socket is marked as -1. */ 29 newconn = netconn_alloc(conn->type, conn->callback); // 新建一个netconn 30 if (newconn == NULL) { 31 return ERR_MEM; 32 } 33 newconn->pcb.tcp = newpcb; 34 setup_tcp(newconn); // 这个函数很重要,我们要记住它把newconn和newpcb绑定起来了 35 newconn->err = err; 36 37 if (sys_mbox_trypost(conn->acceptmbox, newconn) != ERR_OK) { // 把newconn丢到了旧conn(即负责listen的pcb的conn)的acceptmbox这个mailbox上 38 /* When returning != ERR_OK, the connection is aborted in tcp_process(), 39 so do nothing here! */ 40 newconn->pcb.tcp = NULL; 41 netconn_free(newconn); 42 return ERR_MEM; 43 } else { 44 /* Register event with callback */ 45 API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); 46 } 47 48 return ERR_OK; 49 }

 

现在我们已知一个新的newconn产生了,并且它放在负责listen的pcb的conn的acceptmbox里。那么谁来取走这个newconn呢? 就是lwip_accept()函数。 lwip_accept()函数就是用户使用socket过程中调用的accept()。 1 /* Below this, the well-known socket functions are implemented. 2 * Use google.com or opengroup.org to get a good description :-) 3 * 4 * Exceptions are documented! 5 */ 6 7 int 8 lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) // 这里的s是负责listen的socket 9 { 10 struct lwip_socket *sock, *nsock; 11 struct netconn *newconn; 12 struct ip_addr naddr; 13 u16_t port; 14 int newsock; 15 struct sockaddr_in sin; 16 err_t err; 17 18 LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d)...\n", s)); 19 sock = get_socket(s); // 从用户可见的int型socket得到协议栈自己维护的socket descriptor 20 if (!sock) 21 return -1; 22 23 if ((sock->flags & O_NONBLOCK) && (sock->rcvevent conn); // 传入的是listen socket指向的conn 30 .................. 31 }

 

我们先来看一下netconn_accept函数: 1 /** 2 * Accept a new connection on a TCP listening netconn. 3 * 4 * @param conn the TCP listen netconn 5 * @return the newly accepted netconn or NULL on timeout 6 */ 7 struct netconn * 8 netconn_accept(struct netconn *conn) 9 { 10 struct netconn *newconn; 11 12 ............. 13 14 #if LWIP_SO_RCVTIMEO 15 if (sys_arch_mbox_fetch(conn->acceptmbox, (void *)&newconn, conn->recv_timeout) == SYS_ARCH_TIMEOUT) { 16 newconn = NULL; 17 } else 18 #else 19 sys_arch_mbox_fetch(conn->acceptmbox, (void *)&newconn, 0); // 这里从listen socket->conn的acceptmbox取出1个conn,当然就是socket用户在调用listen()后,listen()通过accept_function()函数放入的新conn,这是和listen socket->conn不同的新conn,这里名字是newconn 20 #endif /* LWIP_SO_RCVTIMEO*/ 21 .................. 22 23 return newconn; 24 }

 

所以,netconn_accept取出了client端连接server后server为这一对peer生成的newconn。 我们接着回到lwip_accept()函数: 1 /* Below this, the well-known socket functions are implemented. 2 * Use google.com or opengroup.org to get a good description :-) 3 * 4 * Exceptions are documented! 5 */ 6 7 int 8 lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen) // 这里的s是负责listen的socket 9 { 10 ................. 11 newconn = netconn_accept(sock->conn); // 传入的是listen socket指向的conn 12 if (!newconn) { 13 LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) failed, err=%d\n", s, sock->conn->err)); 14 sock_set_errno(sock, err_to_errno(sock->conn->err)); 15 return -1; 16 } 17 18 /* get the IP address and port of the remote host */ 19 err = netconn_peer(newconn, &naddr, &port); // 从newconn里得到client端ip addr和port号,实际是从newconn绑定的pcb里获得 20 if (err != ERR_OK) { 21 netconn_delete(newconn); 22 sock_set_errno(sock, err_to_errno(err)); 23 return -1; 24 } 25 26 /* Note that POSIX only requires us to check addr is non-NULL. addrlen must 27 * not be NULL if addr is valid. 28 */ 29 if (NULL != addr) { // 如果实参addr地址不是NULL,就把client端的ip地址和port写入addr指向的地址,作为这个函数的返回值之一。但是很多时候这个addr参数用户都会设置为NULL,不需要这个信息。 30 LWIP_ASSERT("addr valid but addrlen NULL", addrlen != NULL); 31 memset(&sin, 0, sizeof(sin)); 32 sin.sin_len = sizeof(sin); 33 sin.sin_family = AF_INET; 34 sin.sin_port = htons(port); 35 sin.sin_addr.s_addr = naddr.addr; 36 37 if (*addrlen > sizeof(sin)) 38 *addrlen = sizeof(sin); 39 40 MEMCPY(addr, &sin, *addrlen); 41 } 42 43 newsock = alloc_socket(newconn); // 对于一个client和server的peer,现在有了新的pcb、新的conn,还需要一个新的socket给用户用!并且这个函数还把newconn和socket绑定起来了,从新socket能得到newconn。 44 if (newsock == -1) { 45 netconn_delete(newconn); 46 sock_set_errno(sock, ENFILE); 47 return -1; 48 } 49 LWIP_ASSERT("invalid socket index", (newsock >= 0) && (newsock callback = event_callback; 51 nsock = &sockets[newsock]; 52 LWIP_ASSERT("invalid socket pointer", nsock != NULL); 53 54 sys_sem_wait(socksem); 55 /* See event_callback: If data comes in right away after an accept, even 56 * though the server task might not have created a new socket yet. 57 * In that case, newconn->socket is counted down (newconn->socket--), 58 * so nsock->rcvevent is >= 1 here! 59 */ 60 nsock->rcvevent += -1 - newconn->socket; 61 newconn->socket = newsock; // 从newconn能找到new socket 62 sys_sem_signal(socksem); 63 64 LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d addr=", s, newsock)); 65 ip_addr_debug_print(SOCKETS_DEBUG, &naddr); 66 LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", port)); 67 68 sock_set_errno(sock, 0); 69 return newsock; 70 } lwip_accept()函数返回了一个new socket,这个socket从listen socket而来,是server专为listen到的client准备的一个socket,可以认为是为这一对通路单独服务的server端socket。我们先把它叫做专属socket。    accept()过程结束,返回的socket用于接下来的recv()。      

posted on 2015-01-06 23:44  日拱一卒编程大法  阅读(7471)  评论(0)  编辑  收藏  举报



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3